package io.gitee.minelx.commontools.date;

import java.util.Arrays;
import java.util.Calendar;
import java.util.List;

public enum ClockUnit {
    YEAR(Calendar.YEAR, 0, 1),
    MONTH(Calendar.MONTH, -1, 1),
    DAY(Calendar.DAY_OF_MONTH, 0, 1),
    HOUR(Calendar.HOUR_OF_DAY, 0, 0),
    MINUTE(Calendar.MINUTE, 0, 0),
    SECOND(Calendar.SECOND, 0, 0),
    MILLISECOND(Calendar.MILLISECOND, 0, 0),
    ;

    private final int calendarConstant;

    private final int calendarValueForwardOffset;

    private final int defaultValue;

    ClockUnit(int calendarConstant, int calendarValueForwardOffset, int defaultValue) {
        this.calendarConstant = calendarConstant;
        this.calendarValueForwardOffset = calendarValueForwardOffset;
        this.defaultValue = defaultValue;
    }

    public Clock at(int... values) {
        Calendar result = Calendar.getInstance();
        set(result, values);
        return new Clock(result);
    }

    void set(Calendar target, int value) {
        target.set(calendarConstant, value + calendarValueForwardOffset);
    }

    void add(Calendar target, int amount) {
        target.add(calendarConstant, amount);
    }

    int defaultValue() {
        return defaultValue;
    }

    boolean isDefaultValue(Calendar calendar) {
        return defaultValue == calendar.get(calendarConstant) - calendarValueForwardOffset;
    }

    List<ClockUnit> levelsBelow() {
        List<ClockUnit> values = Arrays.asList(values());
        return values.subList(ordinal() + 1, values.size());
    }

    List<ClockUnit> levelsRemaining() {
        List<ClockUnit> values = Arrays.asList(values());
        return values.subList(0, ordinal() + 1);
    }

    void set(Calendar instance, int[] values) {
        List<ClockUnit> levelsRemaining = levelsRemaining();
        if (values.length != levelsRemaining.size()) {
            throw new IllegalArgumentException("values count mismatch to the levels remaining of " + this + ".");
        }
        for (int levelAt = 0; levelAt < levelsRemaining.size(); levelAt++) {
            ClockUnit clockUnit = levelsRemaining.get(levelAt);
            clockUnit.set(instance, values[levelAt]);
        }
        // stash the levels below to default value
        levelsBelow().forEach(clockUnit -> clockUnit.set(instance, clockUnit.defaultValue()));
    }
}
